﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;

namespace SharpEntities
{
    public class EntityFactory
    {
        static EntityFactory()
        {
            EntityInfoMap = new Dictionary<Type, EntityInfo>();
        }

        static Dictionary<Type, EntityInfo> EntityInfoMap { get; set; }

        static EntityInfo GetEntityInfo(Type type)
        {
            if (EntityInfoMap.ContainsKey(type))
            {
                return EntityInfoMap[type];
            }
            else
            {
                EntityInfo entityInfo = new EntityInfo();
                EntityInfoMap[type] = entityInfo;

                entityInfo.Properties = type.FindMembers(MemberTypes.Property, BindingFlags.Public | BindingFlags.Instance, new MemberFilter(DelegateToSearchCriteria), null);

                for (int k = 0; k < entityInfo.Properties.Length; k++)
                {
                    object[] attribs = entityInfo.Properties[k].GetCustomAttributes(typeof(ColumnMappingAttribute), false);
                    entityInfo.Attributes[entityInfo.Properties[k]] = attribs;
                }

                return entityInfo;
            }
        }

        public static List<T> GetObjects<T>(DataRowCollection rows)
        {
            List<T> objs = new List<T>();
            EntityInfo entityInfo = GetEntityInfo(typeof(T));

            for (int k = 0; k < rows.Count; k++)
            {
                T obj = GetObject<T>(rows[k], entityInfo);
                objs.Add(obj);
            }

            return objs;
        }

        public static T GetObject<T>(DataRow row)
        {
            EntityInfo entityInfo = GetEntityInfo(typeof(T));
            return GetObject<T>(row, entityInfo);
        }

        public static T GetObject<T>(DataRow row, EntityInfo entityInfo)
        {
            Type t = typeof(T);
            T obj = (T)t.Assembly.CreateInstance(t.FullName);

            if(obj is IEntity)
            {
                IEntity entity = (IEntity) obj;
                entity.DataRow = row;
            }

            for (int k = 0; k < entityInfo.Properties.Length; k++)
            {
                InitProperty(entityInfo.Properties[k], ref obj, row, entityInfo);
            }

            return obj;
        }

        private static void InitProperty<T>(MemberInfo memberInfo, ref T obj, DataRow row, EntityInfo entityInfo)
        {
            object[] attribs = entityInfo.Attributes[memberInfo];

            if (attribs != null && attribs.Length > 0)
            {
                ColumnMappingAttribute ColumnMappingAttribute = (ColumnMappingAttribute)attribs[0];
                string columnName = ColumnMappingAttribute.ColumnName;

                if (memberInfo is PropertyInfo)
                {
                    PropertyInfo propertyInfo = (PropertyInfo)memberInfo;

                    object[] args = new object[1];

                    if (propertyInfo.PropertyType == typeof(string))
                    {
                        args[0] = row[columnName].ToString();
                    }
                    else if (propertyInfo.PropertyType == typeof(char))
                    {
                        args[0] = Convert.ToChar(row[columnName]);
                    }
                    else if (propertyInfo.PropertyType == typeof(bool))
                    {
                        args[0] = Convert.ToBoolean(row[columnName]);
                    }
                    else if (propertyInfo.PropertyType == typeof(byte))
                    {
                        args[0] = Convert.ToByte(row[columnName]);
                    }
                    else if (propertyInfo.PropertyType == typeof(Int16))
                    {
                        args[0] = Convert.ToInt16(row[columnName]);
                    }
                    else if (propertyInfo.PropertyType == typeof(Int32))
                    {
                        args[0] = Convert.ToInt32(row[columnName]);
                    }
                    else if (propertyInfo.PropertyType == typeof(Int64))
                    {
                        args[0] = Convert.ToInt64(row[columnName]);
                    }
                    else if (propertyInfo.PropertyType == typeof(float))
                    {
                        args[0] = (float)Convert.ToDouble(row[columnName]);
                    }
                    else if (propertyInfo.PropertyType == typeof(double))
                    {
                        args[0] = Convert.ToDouble(row[columnName]);
                    }
                    else if (propertyInfo.PropertyType == typeof(decimal))
                    {
                        args[0] = Convert.ToDecimal(row[columnName]);
                    }
                    else if (propertyInfo.PropertyType == typeof(DateTime))
                    {
                        args[0] = Convert.ToDateTime(row[columnName]);
                    }
                    else
                    {
                        throw new ArgumentException("Unsupported data type:" + propertyInfo.PropertyType.Name);
                    }

                    Type t = typeof(T);
                    t.InvokeMember(memberInfo.Name, BindingFlags.SetProperty, null, obj, args);

                }
            }
        }

        private static bool DelegateToSearchCriteria(MemberInfo objMemberInfo, Object objSearch)
        {
            object[] attribs = objMemberInfo.GetCustomAttributes(typeof(ColumnMappingAttribute), false);

            if (attribs != null && attribs.Length > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

    }
}
